module decoder(
    input  [0:0]  CLK, RST_N,
    input  [0:0]  WR_SOP_I, WR_EOP_I, WR_VLD_I, 
    input  [31:0] WR_DATA_I,
    de_cc_bus.de    de_wr_master_ports
//    input  [2:0]  de_wr_master_ports.VLD,
//    output [0:0]  de_wr_master_ports.REQ,
//    output [3:0]  de_wr_master_ports.DATA_DEST,
//    output [2:0]  de_wr_master_ports.DATA_PRIO,
//    output [9:0]  de_wr_master_ports.DATA_SIZE,
//    wr_bus.master de_wr_master_ports

);

localparam WR_IDLE = 2'b00, WR_S1 = 2'b01, WR_FIFO0 = 2'b10, WR_FIFO1 = 2'b11; //WR_S1:to analyse frame header, WR_S2:en-crc and store data to fifo
localparam RD_IDLE = 3'b000, RD_FIFO0_WAITING = 3'b001, RD_FIFO1_WAITING = 3'b010, RD_SOP = 3'b011, RD_FIFO0= 3'b100, RD_FIFO1 = 3'b101;
reg  [1:0]  wr_state;
reg  [2:0]  rd_state;
wire [31:0] crc_wr_data_in;
wire [35:0] crc_wr_data_out;
wire        data_vld;
reg         data_crc_vld ;
reg         fifo0_used,fifo1_used;
wire        vld_to_fifo0, vld_to_fifo1;
wire        write_fifo;
wire        wr_fifo0, wr_fifo1;
wire        fifo0_empty, fifo1_empty;
wire        data_clear;
wire        fifo0_clear, fifo1_clear;
wire [35:0] fifo0_rd_data, fifo1_rd_data;
reg         fifo0_rd_en, fifo1_rd_en;
reg  [2:0]  temp_vld;//to store vld in rd fsm
reg         temp_fifo_assigned;//signal to judge which fifo to write, same logic like signal fifoX_used 

//-------------------------------write FSM
always@(posedge CLK or negedge RST_N)begin
    if(!RST_N)begin
        wr_state <= WR_IDLE;
        temp_fifo_assigned <= 1'b0;
    end
    else begin
        case(wr_state)
            WR_IDLE: begin
                    if(WR_SOP_I)begin
                        wr_state <= WR_S1;
                        if(!fifo0_used)
                            temp_fifo_assigned <= 1'b0;
                        else if(!fifo1_used)                
                            temp_fifo_assigned <= 1'b1;
                    end
                  end
            WR_S1: begin 
                    if(!temp_fifo_assigned)
                        wr_state <= WR_FIFO0;
                    else
                        wr_state <= WR_FIFO1;
                   end
            WR_FIFO0: begin
                        if(fifo0_clear || WR_EOP_I)
                            wr_state <= WR_IDLE;
                      end
            WR_FIFO1: begin
                        if(fifo1_clear || WR_EOP_I)
                            wr_state <= WR_IDLE;
                      end
        endcase
    end
end
//analyse frame header
assign de_wr_master_ports.REQ = (wr_state == WR_IDLE) && WR_SOP_I; //pull to 1 ahead of frame header for 1T
assign de_wr_master_ports.DATA_DEST = {4{(wr_state == WR_S1)}} & WR_DATA_I[3:0];
assign de_wr_master_ports.DATA_PRIO = {3{(wr_state == WR_S1)}} & WR_DATA_I[6:4];
assign de_wr_master_ports.DATA_SIZE = {10{(wr_state == WR_S1)}} & WR_DATA_I[16:7]; 

//-------------------------------CRC
assign crc_wr_data_in = {32{(wr_state == WR_FIFO0 || wr_state == WR_FIFO1) && (WR_VLD_I)}} & WR_DATA_I;


en_crc#(.DATA_WIDTH(32), .CRC_WIDTH(4))u_en_crc( //1T delay
    .CLK         (CLK),
    .RST_N       (RST_N),
    .I_DATA      (crc_wr_data_in),
    .CRC_OUT(crc_wr_data_out)
);

//-------------------------------store wr_data to FIFO
assign data_vld = WR_VLD_I && ((wr_state == WR_FIFO0) || (wr_state == WR_FIFO1)) ;

always@(posedge CLK or negedge RST_N)begin //1T delay to align crc vld with fifo write enable
    if(!RST_N)
        data_crc_vld <= 1'b0;
    else
        data_crc_vld <= data_vld;
end

always@(posedge CLK or negedge RST_N)begin
    if(!RST_N)begin
        fifo0_used <= 0;
        fifo1_used <= 0;
    end
    else begin
        if(WR_SOP_I)begin
            if(!fifo0_used)begin
                fifo0_used <= 1'b1;
            end
            else if(!fifo1_used)begin
                fifo1_used <= 1'b1;
            end
        end
        else if((rd_state == RD_FIFO0 && fifo0_empty) || (fifo0_clear))begin //*********reset fifo0/1_used with help of rd_data FSM    
            fifo0_used <= 1'b0;
        end
        else if(rd_state == RD_FIFO1 && fifo1_empty || (fifo1_clear))begin
            fifo1_used <= 1'b0;
        end
    end
end
//------------------------------- the coming signal <vld> belongs to which fifo
assign vld_to_fifo0 = ((fifo0_used && !fifo1_used) || (fifo0_used && fifo1_used && (wr_state == WR_FIFO1)));
assign vld_to_fifo1 = ((!fifo0_used && fifo1_used) || (fifo0_used && fifo1_used && (wr_state == WR_FIFO0)));


//-------------------------------write fifo enable
assign write_fifo = data_crc_vld;
assign wr_fifo0 = write_fifo && (wr_state == WR_FIFO0);
assign wr_fifo1 = write_fifo && (wr_state == WR_FIFO1);

//-------------------------------clear fifo0 or fifo1
assign data_clear = (de_wr_master_ports.VLD == 3'b001);
assign fifo0_clear =  vld_to_fifo0 && data_clear; //vld_i == 3'b001 influences fifo writing FSM **** caution!!! loading
assign fifo1_clear =  vld_to_fifo1 && data_clear;

sync_fifo#(.FIFO_WIDTH(36), .FIFO_DEPTH(24)) u_sync_fifo_0(
    .CLK    (CLK),
    .RST_N  (RST_N),
    .WR_EN  (wr_fifo0),
    .WR_DATA(crc_wr_data_out),
    .RD_EN  (fifo0_rd_en),
    .CLEAR  (fifo0_clear),
    .FLUSH  (1'b0),
    .RD_DATA(fifo0_rd_data),
    .FULL   (),//suspended
    .EMPTY  (fifo0_empty)
);
sync_fifo#(.FIFO_WIDTH(36), .FIFO_DEPTH(24)) u_sync_fifo_1(
    .CLK    (CLK),
    .RST_N  (RST_N),
    .WR_EN  (wr_fifo1),
    .WR_DATA(crc_wr_data_out),
    .RD_EN  (fifo1_rd_en),
    .CLEAR  (fifo1_clear),
    .FLUSH  (1'b0),
    .RD_DATA(fifo1_rd_data),
    .FULL   (),//suspended
    .EMPTY  (fifo1_empty)
);
//-------------------------------fetch data from FIFO
/*
always@(posedge CLK or negedge RST_N)begin
    if(!RST_N)begin
        rd_state <= RD_IDLE;
        de_wr_master_ports.WR_SOP <= 1'b0;
        de_wr_master_ports.WR_VLD <= 1'b0;
        temp_vld <= 3'b000;
    end
    else begin
        case(rd_state)
            RD_IDLE:begin
                    if(de_wr_master_ports.VLD == 3'b010 || de_wr_master_ports.VLD ==3'b100)begin
                        rd_state <= RD_SOP;
                        de_wr_master_ports.WR_SOP <= 1'b1;
                        temp_vld <= de_wr_master_ports.VLD;
                    end
                end
            RD_SOP:begin
                        de_wr_master_ports.WR_SOP <= 1'b0;
                        de_wr_master_ports.WR_VLD <= 1'b1;
                        temp_vld <= 3'b000;
                    if(vld_to_fifo0 && (temp_vld == 3'b010 || temp_vld == 3'b100))begin
                        rd_state <= RD_FIFO0;
                        fifo0_rd_en <= 1'b1;
                        de_wr_master_ports.WR_DATA <= fifo0_rd_data;
                    end
                    else if(vld_to_fifo1 && (temp_vld == 3'b010 || temp_vld == 3'b100))begin
                        rd_state <= RD_FIFO1;
                        fifo1_rd_en <= 1'b1; // remember reset to 0 in the right time
                        de_wr_master_ports.WR_DATA <= fifo1_rd_data;
                    end
                   end
            RD_FIFO0:begin
                        if(fifo0_empty)begin
                            rd_state <= RD_IDLE;
                            de_wr_master_ports.WR_VLD <= 1'b0;
                            fifo0_rd_en <= 1'b0;
                        end
                        else begin
                        end
                     end
            RD_FIFO1:begin
                        if(fifo1_empty)begin
                            rd_state <= RD_IDLE;
                            de_wr_master_ports.WR_VLD <= 1'b0;
                            fifo1_rd_en <= 1'b0;
                        end
                        else begin
                        end
                     end
        endcase
    end
end
*/
//rd data fsm version 2.0always@(posedge CLK or negedge RST_N)begin
always@(posedge CLK or negedge RST_N)begin
    if(!RST_N)begin
        rd_state <= RD_IDLE;
        de_wr_master_ports.WR_SOP <= 1'b0;
        de_wr_master_ports.WR_VLD <= 1'b0;
        temp_vld <= 3'b000;
    end
    else begin
        case(rd_state)
            RD_IDLE:begin
                    if(de_wr_master_ports.VLD == 3'b010 || de_wr_master_ports.VLD ==3'b100)begin
                        if(vld_to_fifo0 && fifo0_empty)
                            rd_state <= RD_FIFO0_WAITING;
                        else if(vld_to_fifo1 && fifo1_empty)
                            rd_state <= RD_FIFO1_WAITING;
                        else begin
                            rd_state <= RD_SOP;
                            de_wr_master_ports.WR_SOP <= 1'b1;
                        end
                        temp_vld <= de_wr_master_ports.VLD;
                    end
                end
            RD_FIFO0_WAITING:begin
                    if(!fifo0_empty)begin
                        rd_state <= RD_SOP;
                        de_wr_master_ports.WR_SOP <= 1'b1;
                    end
                    end
            RD_FIFO1_WAITING:begin
                    if(!fifo1_empty)begin
                        rd_state <= RD_SOP;
                        de_wr_master_ports.WR_SOP <= 1'b1;
                    end
                    end
            RD_SOP:begin
                        de_wr_master_ports.WR_SOP <= 1'b0;
                        de_wr_master_ports.WR_VLD <= 1'b1;
                        temp_vld <= 3'b000;
                    if(vld_to_fifo0 && (temp_vld == 3'b010 || temp_vld == 3'b100))begin
                        rd_state <= RD_FIFO0;
                        fifo0_rd_en <= 1'b1;
                        de_wr_master_ports.WR_DATA <= fifo0_rd_data;
                    end
                    else if(vld_to_fifo1 && (temp_vld == 3'b010 || temp_vld == 3'b100))begin
                        rd_state <= RD_FIFO1;
                        fifo1_rd_en <= 1'b1; // remember reset to 0 in the right time
                        de_wr_master_ports.WR_DATA <= fifo1_rd_data;
                    end
                   end
            RD_FIFO0:begin
                        de_wr_master_ports.WR_DATA <= fifo0_rd_data;
                        if(fifo0_empty)begin
                            rd_state <= RD_IDLE;
                            de_wr_master_ports.WR_VLD <= 1'b0;
                            fifo0_rd_en <= 1'b0;
                        end
                        else begin
                        end
                     end
            RD_FIFO1:begin
                        de_wr_master_ports.WR_DATA <= fifo1_rd_data;
                        if(fifo1_empty)begin
                            rd_state <= RD_IDLE;
                            de_wr_master_ports.WR_VLD <= 1'b0;
                            fifo1_rd_en <= 1'b0;
                        end
                        else begin
                        end
                     end
        endcase
    end
end


assign de_wr_master_ports.WR_EOP = (((rd_state == RD_FIFO0) && fifo0_empty)||((rd_state == RD_FIFO1) && fifo1_empty));

endmodule
